/**
 * 
 */
package mdp.solve;

import java.util.ArrayList;
import java.util.concurrent.ArrayBlockingQueue;

import util.Pair;
import util.Timer;

import dd.discrete.DD;
import mdp.id.ADDHandler;
import mdp.id.RDDL2DD;
import mdp.id.RDDL2DD.DEBUG;
import mdp.id.RDDL2DD.ORDER;

/**
 * @author NR
 *
 * TODO
 */
public class FAR<T extends RDDL2DD> implements Runnable{
	
	private static double	EPSILON	= 0;
	private T _handler;
	private ArrayBlockingQueue<Pair<T, Integer>> _bq = null;
	/**
	 * 
	 */
	public FAR(T handler, String domain, String instance, double epsilon,
			ArrayBlockingQueue<Pair<T, Integer>> bq) {
		_bq = bq;
		EPSILON = epsilon;
		_handler = handler;
		_handler.buildFromFile(domain, instance);
	}

	/* (non-Javadoc)
	 * @see java.lang.Runnable#run()
	 */
//	@Override
	public void run() {
		
		int _valueDD = _handler.DD_ZERO;
		int _q = 0;
		
		int iter = 0;
		boolean done = false;
		Timer solutionTimer = new Timer();
		
		
		while( !done ) {
			
			solutionTimer.ResumeTimer();
			
			int Vprev = _valueDD;
			int Vprimed = _handler.swapForPrimes(_valueDD);
			Pair<Integer, Integer> vq = _handler.regress(Vprimed, null, true, Vprev); 
			
			int Vnext = vq._o1;
			_q = vq._o2;
			_valueDD = Vnext;
			
			int diff = _handler._context.applyInt(Vnext, Vprev, DD.ARITH_MINUS);
			
			double error = Math.max(
					Math.abs(_handler.getMax(diff)), 
					Math.abs(_handler.getMin(diff) == _handler.getNegInf() ? 0 : _handler.getMin(diff)));
			++iter;
			
			if( error < _handler.getStoppingValue(EPSILON) ) {
				done = true;
				_handler.saveThis(_q);
			}
			
			_handler.flushCaches(false, _valueDD);
			
			solutionTimer.PauseTimer();
			
			System.out.println("Iteration " + iter + " " + error + " " + solutionTimer.GetElapsedTimeInMinutes() + " " + _handler.getSize(_valueDD));
			
		}
		
		solutionTimer.ResumeTimer();
		int _policyDD = _handler.makePolicy(_valueDD, _q);
		solutionTimer.PauseTimer();
		
		_handler.setValueDD(_valueDD);
		_handler.setPolicyDD(_policyDD);
		
		ArrayList stats = new ArrayList();
		try {
			stats.add(_handler.playDD(_policyDD));
		}catch(Exception e) {
			e.printStackTrace();
		}
		
		stats.add(0.0d);
		stats.add(solutionTimer.GetElapsedTimeInMinutes());
		stats.add(_handler.getSize(_valueDD));
		stats.add(_handler.getSize(_policyDD));
		
		System.out.println(stats);
//		_handler.showGraph(_policyDD);
		
		if( _bq != null ) {
			try
			{
				_bq.put(new Pair<T, Integer>(_handler, _valueDD));
//				_bq.put(new Pair<T, Integer>(_handler, _policyDD));
			} catch (InterruptedException e)
			{
				e.printStackTrace();
			}
		
		}
	}
	
	public static void main(String[] args) throws InterruptedException {
		ADDHandler handle = new ADDHandler(DEBUG.PROBLEM_INFO , ORDER.GUESS );
		Runnable vic = 
			new FAR<ADDHandler>(handle, args[0], args[1], Double.parseDouble(args[2]), null);
		Thread vicWorker = new Thread(vic, "VIC");
		vicWorker.start();
		vicWorker.join();
	}
	
}
